package com.ygp.dtp.core.notify;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import com.ygp.dtp.common.config.properties.DtpProperties;
import com.ygp.dtp.common.dto.NotifyItem;
import com.ygp.dtp.common.enums.NotifyTypeEnum;
import com.ygp.dtp.common.enums.QueueTypeEnum;
import com.ygp.dtp.common.enums.RejectedTypeEnum;
import com.ygp.dtp.common.holder.ApplicationContextHolder;
import com.ygp.dtp.core.context.DynamicThreadPoolContext;
import com.ygp.dtp.core.context.DynamicThreadPoolContextHolder;
import com.ygp.dtp.core.handler.NotifierHandler;
import com.ygp.dtp.core.thread.DynamicThreadPoolExecutor;
import com.ygp.dtp.core.thread.ThreadPoolBuilder;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;

/**
 * @description: 预警管理类
 * @motto: 代码源于生活，高于生活艺术
 * @author: zhouhengzhe
 * @date: 2022/8/10 15:34
 * @since 1.0
 **/
@Slf4j
public class AlarmManager {


    private static final ExecutorService ALARM_EXECUTOR = ThreadPoolBuilder.builder()
            .threadPoolName("dtp-alarm")
            .threadFactory("dtp-alarm")
            .corePoolSize(2)
            .maximumPoolSize(4)
            .workQueue(QueueTypeEnum.LINKED_BLOCKING_QUEUE.getName(), 2000, false)
            .rejectedExecutionHandler(RejectedTypeEnum.ABORT_POLICY.getCode())
            .buildWithTtl();

    public static void triggerAlarm(Runnable runnable) {
        ALARM_EXECUTOR.execute(runnable);
    }

    public static void doAlarm(DynamicThreadPoolExecutor executor, List<NotifyTypeEnum> typeEnums) {
        typeEnums.forEach(x -> doAlarm(executor, x));
    }

    public static void doAlarm(DynamicThreadPoolExecutor executor, NotifyTypeEnum typeEnum) {

        boolean triggerCondition = false;
        if (typeEnum == NotifyTypeEnum.REJECT) {
            triggerCondition = checkReject(executor);
        } else if (typeEnum == NotifyTypeEnum.CAPACITY) {
            triggerCondition = checkCapacity(executor);
        } else if (typeEnum == NotifyTypeEnum.LIVE_NESS) {
            triggerCondition = checkLiveness(executor);
        }
        if (!triggerCondition) {
            return;
        }

        boolean ifAlarm = AlarmLimiter.ifAlarm(executor, typeEnum.getCode());
        if (!ifAlarm) {
            log.warn("DynamicTp notify, alarm limit, dtpName: {}, type: {}",
                    executor.getThreadPoolName(), typeEnum.getCode());
            return;
        }
        DtpProperties dtpProperties = ApplicationContextHolder.getBean(DtpProperties.class);
        NotifyItem notifyItem = NotifyHelper.getNotifyItem(executor, typeEnum);
        DynamicThreadPoolContext dtpContext = DynamicThreadPoolContext.builder()
                .dynamicThreadPoolExecutor(executor)
                .platforms(dtpProperties.getPlatforms())
                .notifyItem(notifyItem)
                .build();
        DynamicThreadPoolContextHolder.set(dtpContext);
        AlarmLimiter.putVal(executor, typeEnum.getCode());
        NotifierHandler.getInstance().sendAlarm(typeEnum);
    }


    private static boolean checkLiveness(DynamicThreadPoolExecutor executor) {

        NotifyItem notifyItem = NotifyHelper.getNotifyItem(executor, NotifyTypeEnum.LIVE_NESS);
        if (Objects.isNull(notifyItem)) {
            return false;
        }

        int maximumPoolSize = executor.getMaximumPoolSize();
        double div = NumberUtil.div(executor.getActiveCount(), maximumPoolSize, 2) * 100;
        return notifyItem.isEnabled() && div >= notifyItem.getThreshold();
    }

    private static boolean checkCapacity(DynamicThreadPoolExecutor executor) {
        BlockingQueue<Runnable> workQueue = executor.getQueue();
        if (CollUtil.isEmpty(workQueue)) {
            return false;
        }

        NotifyItem notifyItem = NotifyHelper.getNotifyItem(executor, NotifyTypeEnum.CAPACITY);
        if (Objects.isNull(notifyItem)) {
            return false;
        }

        int queueCapacity = executor.getQueueCapacity();
        double div = NumberUtil.div(workQueue.size(), queueCapacity, 2) * 100;
        return notifyItem.isEnabled() && div >= notifyItem.getThreshold();
    }

    private static boolean checkReject(DynamicThreadPoolExecutor executor) {
        NotifyItem notifyItem = NotifyHelper.getNotifyItem(executor, NotifyTypeEnum.REJECT);
        if (Objects.isNull(notifyItem)) {
            return false;
        }

        int rejectCount = executor.getRejectCount();
        return notifyItem.isEnabled() && rejectCount >= notifyItem.getThreshold();
    }
}
